package com.elecbook.auth.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.elecbook.auth.config.RedissionConfig;
import com.elecbook.auth.config.properties.WeChatProperties;
import com.elecbook.auth.model.dto.LoginDTO;
import com.elecbook.auth.model.po.User;
import com.elecbook.auth.mapper.UserMapper;
import com.elecbook.auth.utils.HttpClientUtil;
import com.elecbook.base.config.SystemPropertiesConfig;
import com.elecbook.base.exception.BaseInfoException;
import com.elecbook.rpc.feignclient.SystemServiceClient;
import com.elecbook.system.model.dto.PropertiesDTO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.net.UnknownServiceException;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * description 微信认证service接口实现类
 *
 * @author zken
 * CreateDate 2024/11/2 17:10:18
 */
@Service("auth_weixin")
public class WxLoginAuthService extends AbstractAuthService {

    @Resource
    UserMapper userMapper;
    @Resource
    WeChatProperties weChatProperties;
    @Autowired
    SystemServiceClient systemServiceClient;

    /**
     * description 认证验证方法，根据loginDTO中的openId进行验证
     *
     * @author zken
     * CreateDate 2024/11/2 17:08:26
     */
    @Transactional
    @Override
    public UserDetails login(LoginDTO loginDTO) {
        String lockKey = loginDTO.getWxOpenid();
        long waitTimeout = 5L;
        long leaseTime = 15L;

        // 3.获取锁对象实例（无法保证是按线程的顺序获取到）
        RedissonClient redisson = RedissionConfig.getRedissonClientYaml();
        RLock rLock = redisson.getLock(lockKey);

        try {
            /**
             * 4.尝试获取锁
             * waitTimeout 尝试获取锁的最大等待时间，超过这个值，则认为获取锁失败
             * leaseTime   锁的持有时间,超过这个时间锁会自动失效（值应设置为大于业务处理的时间，确保在锁有效期内业务能处理完）
             * trylock并不会抛出异常
             */
            boolean res = rLock.tryLock((long) waitTimeout, (long) leaseTime, TimeUnit.SECONDS);
            if (res) {
                return wxLoginSupport(loginDTO);
            } else {
                // 获取锁失败
                System.out.println("aquire lock fail");
                throw new BaseInfoException("登录异常");
            }
        } catch (Exception e) {
            System.out.println("error");
            throw new RuntimeException("业务执行时刻发生了错误");
        } finally {
            //无论业务是否执行成功如何, 最后都要解锁
            rLock.unlock();
        }
    }

    private UserDetails wxLoginSupport(LoginDTO loginDTO) {
        //成功获得锁，在这里处理业务
        System.out.println("你好！！！，你来到了这个需要锁才能进入的地方");

        // 解压获取的json字符串，获取字段是 openid
        String openid = getOpenid(loginDTO.getWxCode());
        // MARK 定义真正的openId，用它来进行用户的真实登录即可
        //        String openid = "2333";

        // 你需要保证openid一定存在
        if (openid == null) {
            throw new UsernameNotFoundException("不明确的登录请求，被微信拒绝");
        }

        // 不为空，检测是否为新用户
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getOpenid, openid);

        User user = userMapper.selectOne(wrapper);
        // 用户不存在的情况加入新用户
        if (user == null) {
            user = new User();
            user.setOpenid(openid);
            // 设置默认头像
            PropertiesDTO dto = new PropertiesDTO();
            dto.setKey(SystemPropertiesConfig.defaultImageUri);
            String defaultImageUri = systemServiceClient.getValue(dto);
            user.setAvatar(defaultImageUri);
            dto.setKey(SystemPropertiesConfig.defaultUserName);
            // 设置默认名称
            String defaultUsername = systemServiceClient.getValue(dto) +
                    UUID.randomUUID().toString()
                            .replace("-", "").substring(0, 10);
            user.setName(defaultUsername);

            userMapper.insert(user);
        }

        return loadUserByUserPo(user);
    }


    final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session"; // 微信的openid的获取网址

    public String getOpenid(String js_code) {
        HashMap hashMap = new HashMap();
        // 设置请求的参数，这里设置的是get请求的参数（带着？的部分的参数）
        hashMap.put("appid", weChatProperties.getAppid());
        hashMap.put("secret", weChatProperties.getSecret());
        hashMap.put("js_code", js_code);
        hashMap.put("grant_type", weChatProperties.getGrantType());
        // 使用自封工具类获取openid
        String res_json = HttpClientUtil.doGet(WX_LOGIN, hashMap);
        // 解压获取的json字符串，获取字段是 openid
        JSONObject jsonObject = JSONObject.parseObject(res_json);
        String openid = jsonObject.getString("openid");
        return openid;
    }
}
